package core.util.bean;

import java.io.Serializable;
import java.util.Arrays;
import java.util.Iterator;
import java.util.NoSuchElementException;

/**
 * タプル
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 * @param <T> ジェネリックス
 */
public final class Tuple<T> implements Iterable<T>, Serializable {
	/** serialVersionUID */
	private static final long serialVersionUID = 5818916983288475733L;

	/** 値保持配列 */
	private final T[] value;

	/**
	 * コンストラクタ
	 *
	 * @param vals 値
	 */
	@SafeVarargs
	public Tuple(final T... vals) {
		if (vals == null) {
			throw new IllegalArgumentException();
		}

		this.value = Arrays.copyOf(vals, vals.length);
	}

	/**
	 * 長さ取得
	 *
	 * @return 長さ
	 */
	public int length() {
		return this.value.length;
	}

	/**
	 * 取得
	 *
	 * @param loc 位置
	 * @return 値
	 */
	public T get(final int loc) {
		if (loc < 0 || this.value.length <= loc) {
			throw new ArrayIndexOutOfBoundsException(loc);
		}
		return this.value[loc];
	}

	/**
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return Arrays.toString(this.value);
	}

	/**
	 * 配列化
	 *
	 * @return 配列
	 */
	public T[] toArray() {
		return Arrays.copyOf(this.value, this.value.length);
	}

	/**
	 * @see java.lang.Iterable#iterator()
	 */
	@Override
	public Iterator<T> iterator() {
		return new TupleIterator<>(this.value);
	}

	/**
	 * 連結
	 *
	 * @param <T> ジェネリックス
	 * @param val 値
	 * @param tpl タプル
	 * @return 連結結果
	 */
	public static <T> Tuple<T> cons(final T[] val, final Tuple<T> tpl) {
		return cons(new Tuple<>(val), tpl);
	}

	/**
	 * 連結
	 *
	 * @param <T> ジェネリックス
	 * @param tpl タプル
	 * @param val 値
	 * @return 連結結果
	 */
	@SafeVarargs
	public static <T> Tuple<T> cons(final Tuple<T> tpl, final T... val) {
		return cons(tpl, new Tuple<>(val));
	}

	/**
	 * 連結
	 *
	 * @param <T> ジェネリックス
	 * @param tpl1 タプル
	 * @param tpl2 タプル
	 * @return 連結結果
	 */
	public static <T> Tuple<T> cons(final Tuple<T> tpl1, final Tuple<T> tpl2) {
		T[] array = Arrays.copyOf(tpl1.value, tpl1.value.length + tpl2.value.length);
		System.arraycopy(tpl2.value, 0, array, tpl1.length(), tpl2.length());
		return new Tuple<>(array);
	}

	/**
	 * イテレータクラス
	 *
	 * @author Tadashi Nakayama
	 * @version 1.0.0
	 * @param <E> ジェネリックス
	 */
	private static final class TupleIterator<E> implements Iterator<E> {
		/** 現在位置 */
		private int loc = 0;
		/** インスタンス */
		private final E[] value;

		/**
		 * コンストラクタ
		 *
		 * @param val インスタンス
		 */
		public TupleIterator(final E[] val) {
			this.value = val;
		}

		/**
		 * @see java.util.Iterator#hasNext()
		 */
		@Override
		public boolean hasNext() {
			return this.loc < this.value.length;
		}

		/**
		 * @see java.util.Iterator#next()
		 */
		@Override
		public E next() {
			if (this.value.length <= this.loc) {
				throw new NoSuchElementException();
			}

			return this.value[this.loc++];
		}

		/**
		 * @see java.util.Iterator#remove()
		 */
		@Override
		public void remove() {
			throw new UnsupportedOperationException();
		}
	}
}
